package com.srtp.secureprotocol.keychain;

import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringDef;

import com.srtp.secureprotocol.keychain.cipherStorage.CipherStorage;
import com.srtp.secureprotocol.keychain.cipherStorage.CipherStorageBase;
import com.srtp.secureprotocol.keychain.cipherStorage.CipherStorageKeystoreAesCbc;
import com.srtp.secureprotocol.keychain.exceptions.CryptoFailedException;
import com.srtp.secureprotocol.keychain.exceptions.EmptyParameterException;
import com.srtp.secureprotocol.keychain.exceptions.KeyStoreAccessException;
import com.srtp.secureprotocol.keychain.cipherStorage.CipherStorage.DecryptionResult;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.crypto.Cipher;

@SuppressWarnings({"unused", "WeakerAccess", "SameParameterValue"})
public class Keychain {
    public static final String KEYCHAIN_MODULE = "KeychainManager";
    public static final String FINGERPRINT_SUPPORTED_NAME = "Fingerprint";
    public static final String FACE_SUPPORTED_NAME = "Face";
    public static final String IRIS_SUPPORTED_NAME = "Iris";
    public static final String EMPTY_STRING = "";

    private static final String LOG_TAG = Keychain.class.getSimpleName();

    @StringDef({AccessControl.NONE
            , AccessControl.USER_PRESENCE
            , AccessControl.BIOMETRY_ANY
            , AccessControl.BIOMETRY_CURRENT_SET
            , AccessControl.DEVICE_PASSCODE
            , AccessControl.APPLICATION_PASSWORD
            , AccessControl.BIOMETRY_ANY_OR_DEVICE_PASSCODE
            , AccessControl.BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE})
    @interface AccessControl {
        String NONE = "None";
        String USER_PRESENCE = "UserPresence";
        String BIOMETRY_ANY = "BiometryAny";
        String BIOMETRY_CURRENT_SET = "BiometryCurrentSet";
        String DEVICE_PASSCODE = "DevicePasscode";
        String APPLICATION_PASSWORD = "ApplicationPassword";
        String BIOMETRY_ANY_OR_DEVICE_PASSCODE = "BiometryAnyOrDevicePasscode";
        String BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE = "BiometryCurrentSetOrDevicePasscode";
    }

    @interface AuthPromptOptions {
        String TITLE = "title";
        String SUBTITLE = "subtitle";
        String DESCRIPTION = "description";
        String CANCEL = "cancel";
    }

    @interface Maps {
        String ACCESS_CONTROL = "accessControl";
        String ACCESS_GROUP = "accessGroup";
        String ACCESSIBLE = "accessible";
        String AUTH_PROMPT = "authenticationPrompt";
        String AUTH_TYPE = "authenticationType";
        String SERVICE = "service";
        String SECURITY_LEVEL = "securityLevel";
        String RULES = "rules";

        String USERNAME = "username";
        String PASSWORD = "password";
        String STORAGE = "storage";
    }

    @interface Errors {
        String E_EMPTY_PARAMETERS = "E_EMPTY_PARAMETERS";
        String E_CRYPTO_FAILED = "E_CRYPTO_FAILED";
        String E_KEYSTORE_ACCESS_ERROR = "E_KEYSTORE_ACCESS_ERROR";
        String E_SUPPORTED_BIOMETRY_ERROR = "E_SUPPORTED_BIOMETRY_ERROR";
        /**
         * Raised for unexpected errors.
         */
        String E_UNKNOWN_ERROR = "E_UNKNOWN_ERROR";
    }

    /**
     * 支持的加密算法。
     */
    @StringDef({KnownCiphers.AES})
    public @interface KnownCiphers {
        /**
         * AES 加密算法。
         */
        String AES = "KeystoreAESCBC";
    }


    /**
     * 密钥操作规则。
     */
    @StringDef({Rules.AUTOMATIC_UPGRADE, Rules.NONE})
    @interface Rules {
        String NONE = "none";  // 无特殊操作规则
        String AUTOMATIC_UPGRADE = "automaticUpgradeToMoreSecuredStorage";  // 自动升级到更安全的存储
    }
    //endregion

    //region 成员变量
    /**
     * 算法名称到实例的映射
     */
    private final Map<String, CipherStorage> cipherStorageMap = new HashMap<>();
    /**
     * 共享偏好设置存储
     */
    private final PrefsStorage prefsStorage;
    //endregion

    //region 初始化

    /**
     * 默认构造函数
     */
    public Keychain(@NonNull final Context context) {
        prefsStorage = new PrefsStorage(context);  // 初始化共享偏好存储
        // 添加AES-CBC加密存储实现
        addCipherStorageToMap(new CipherStorageKeystoreAesCbc());
    }


    /**
     * 带预热功能的初始化方法
     * 在后台线程预热最佳加密算法，加速后续操作
     */
    public static Keychain withWarming(@NonNull final Context context) {
        final Keychain instance = new Keychain(context);
        // 在后台线程执行加密算法预热
        final Thread warmingUp = new Thread(instance::internalWarmingBestCipher, "keychain-warming-up");
        warmingUp.setDaemon(true);  // 设置为守护线程
        warmingUp.start();  // 启动预热线程
        return instance;
    }

    /**
     * 加密算法预热逻辑：强制加载类并初始化
     */
    private void internalWarmingBestCipher() {
        try {
            final long startTime = System.nanoTime();
            Log.v(KEYCHAIN_MODULE, "warming up started at " + startTime);

            // 获取当前API级别的最佳加密存储
            final CipherStorageBase best = (CipherStorageBase) getCipherStorageForCurrentAPILevel();
            final Cipher instance = best.getCachedInstance();  // 获取密码实例
            final boolean isSecure = best.supportsSecureHardware();  // 检查是否支持安全硬件
            // 确定所需的安全级别
            final SecurityLevel requiredLevel = isSecure ? SecurityLevel.SECURE_HARDWARE : SecurityLevel.SECURE_SOFTWARE;
            best.generateKeyAndStoreUnderAlias("warmingUp", requiredLevel);  // 生成预热密钥
            best.getKeyStoreAndLoad();  // 加载密钥库

            Log.v(KEYCHAIN_MODULE, "warming up takes: " +
                    TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) +
                    " ms");
        } catch (Throwable ex) {
            Log.e(KEYCHAIN_MODULE, "warming up failed!", ex);
        }
    }
    //endregion

    //region Overrides


    //region 公共方法

    /**
     * 存储用户名和密码
     *
     * @param alias    条目别名
     * @param username 用户名
     * @param password 密码
     * @param options  可选参数（安全级别/存储类型等）
     */
    public void setGenericPassword(@NonNull final String alias,
                                   @NonNull final String username,
                                   @NonNull final String password,
                                   @Nullable final HashMap<String, String> options) {
        try {
            throwIfEmptyLoginPassword(username, password);  // 校验非空

            final SecurityLevel level = getSecurityLevelOrDefault(options);  // 获取安全级别
            final CipherStorage storage = getSelectedStorage(options);  // 选择加密存储

            throwIfInsufficientLevel(storage, level);  // 检查存储是否满足安全级别

            // 加密并存储结果
            final CipherStorage.EncryptionResult result = storage.encrypt(alias, username, password, level);
            prefsStorage.storeEncryptedEntry(alias, result);

        } catch (Throwable e) {
            Log.e(KEYCHAIN_MODULE, e.getMessage(), e);
        }
    }


    /**
     * 根据选项选择加密存储
     * 优先使用options指定的存储，否则根据API级别自动选择
     */
    @NonNull
    private CipherStorage getSelectedStorage(@Nullable final HashMap<String, String> options)
            throws CryptoFailedException {
        final String accessControl = getAccessControlOrDefault(options);  // 获取访问控制设置
        final boolean useBiometry = getUseBiometry(accessControl);  // 是否使用生物识别
        final String cipherName = getSpecificStorageOrDefault(options);  // 获取指定存储名称

        CipherStorage result = null;
        // 按名称查找指定存储
        if (null != cipherName) {
            result = getCipherStorageByName(cipherName);
        }
        // 未指定时选择当前API最佳存储
        if (null == result) {
            result = getCipherStorageForCurrentAPILevel(useBiometry);
        }
        return result;
    }

    /**
     * 获取存储的密码
     *
     * @param alias   条目别名
     * @param options 可选参数
     * @return 解密后的密码字符串
     */
    public String getGenericPassword(@NonNull final String alias, @Nullable final HashMap options) {
        try {
            // 从共享偏好获取加密数据
            final PrefsStorage.ResultSet resultSet = prefsStorage.getEncryptedEntry(alias);
            // 获取当前API兼容的加密存储（不使用生物识别）
            final CipherStorage current = getCipherStorageForCurrentAPILevel(false);
            final String rules = getSecurityRulesOrDefault(options);  // 获取安全规则

            // 解密凭证
            final DecryptionResult decryptionResult = decryptCredentials(alias, current, resultSet, rules);
            return decryptionResult.password;

        } catch (KeyStoreAccessException | CryptoFailedException e) {
            Log.e(KEYCHAIN_MODULE, e.getMessage());
        } catch (Throwable fail) {
            Log.e(KEYCHAIN_MODULE, fail.getMessage(), fail);
        }
        return null;
    }


    /**
     * 删除指定别名的密码
     */
    public void resetGenericPassword(@NonNull final String alias) {
        try {
            // 1. 清理加密存储中的密钥
            final PrefsStorage.ResultSet resultSet = prefsStorage.getEncryptedEntry(alias);
            if (resultSet != null) {
                // 根据存储记录获取对应加密实例
                final CipherStorage cipherStorage = getCipherStorageByName(resultSet.cipherStorageName);
                if (cipherStorage != null) {
                    cipherStorage.removeKey(alias);  // 删除密钥
                }
            }
            // 2. 删除共享偏好中的条目
            prefsStorage.removeEntry(alias);

        } catch (KeyStoreAccessException e) {
            Log.e(KEYCHAIN_MODULE, e.getMessage());
        } catch (Throwable fail) {
            Log.e(KEYCHAIN_MODULE, fail.getMessage(), fail);
        }
    }

    //region Helpers

    /**
     * 从选项中获取服务值，如未提供则使用默认别名
     */
    @NonNull
    private static String getServiceOrDefault(@Nullable final HashMap<String, String> options) {
        String service = null;
        // 检查options是否包含SERVICE配置项
        if (null != options && options.containsKey(Maps.SERVICE)) {
            service = options.get(Maps.SERVICE);
        }
        // 返回服务名或默认别名
        return getAliasOrDefault(service);
    }

    /**
     * 获取密钥安全操作规则，默认返回自动升级规则
     *
     * @return 安全规则字符串 (Rules.AUTOMATIC_UPGRADE 或 Rules.NONE)
     */
    @Rules
    @NonNull
    private static String getSecurityRulesOrDefault(@Nullable final HashMap options) {
        // 调用重载方法，默认使用自动升级规则
        return getSecurityRulesOrDefault(options, Rules.AUTOMATIC_UPGRADE);
    }


    /**
     * 获取密钥安全操作规则，可指定默认值
     *
     * @param rule 当未配置时的默认规则
     * @return 从选项获取的规则或默认规则
     */
    @Rules
    @NonNull
    private static String getSecurityRulesOrDefault(@Nullable final HashMap<String, String> options,
                                                    @Rules @NonNull final String rule) {
        String rules = null;
        // 检查RULES配置项（注意：原代码中使用了ACCESS_CONTROL，可能是笔误）
        if (null != options && options.containsKey(Maps.RULES)) {
            rules = options.get(Maps.ACCESS_CONTROL);  // 此处疑似应为 Maps.RULES
        }
        // 未配置时返回默认规则
        return (null == rules) ? rule : rules;
    }

    /**
     * 从选项获取指定的存储类型
     *
     * @return KnownCiphers 注解定义的存储类型名称或null
     */
    @KnownCiphers
    @Nullable
    private static String getSpecificStorageOrDefault(@Nullable final HashMap<String, String> options) {
        String storageName = null;
        // 检查STORAGE配置项
        if (null != options && options.containsKey(Maps.STORAGE)) {
            storageName = options.get(Maps.STORAGE);
        }
        return storageName;
    }

    /**
     * 获取访问控制设置，默认无特殊控制
     *
     * @return AccessControl 注解定义的访问控制类型
     */
    @AccessControl
    @NonNull
    private static String getAccessControlOrDefault(@Nullable final HashMap options) {
        // 调用重载方法，默认无访问控制
        return getAccessControlOrDefault(options, AccessControl.NONE);
    }

    /**
     * 获取访问控制设置，可指定默认值
     *
     * @param fallback 未配置时的默认值
     * @return 访问控制配置值
     */
    @AccessControl
    @NonNull
    private static String getAccessControlOrDefault(@Nullable final HashMap<String, String> options,
                                                    @AccessControl @NonNull final String fallback) {
        String accessControl = null;
        // 检查ACCESS_CONTROL配置项
        if (null != options && options.containsKey(Maps.ACCESS_CONTROL)) {
            accessControl = options.get(Maps.ACCESS_CONTROL);
        }
        // 未配置时返回默认值
        return (null == accessControl) ? fallback : accessControl;
    }


    /**
     * 获取安全等级，默认返回最低安全级别(ANY)
     *
     * @return SecurityLevel 枚举值
     */
    @NonNull
    private static SecurityLevel getSecurityLevelOrDefault(@Nullable final HashMap options) {
        // 调用重载方法，默认安全级别为ANY
        return getSecurityLevelOrDefault(options, SecurityLevel.ANY.name());
    }


    /**
     * 获取安全等级，可指定默认值
     *
     * @param fallback 未配置时的默认安全级别
     * @return 解析后的安全等级枚举
     */
    @NonNull
    private static SecurityLevel getSecurityLevelOrDefault(@Nullable final HashMap<String, String> options,
                                                           @NonNull final String fallback) {
        String minimalSecurityLevel = null;
        // 检查SECURITY_LEVEL配置项
        if (null != options && options.containsKey(Maps.SECURITY_LEVEL)) {
            minimalSecurityLevel = options.get(Maps.SECURITY_LEVEL);
        }
        // 未配置时使用默认值
        if (null == minimalSecurityLevel) {
            minimalSecurityLevel = fallback;
        }
        // 将字符串转换为SecurityLevel枚举
        return SecurityLevel.valueOf(minimalSecurityLevel);
    }
    //endregion

    //region Implementation

    /**
     * 判断访问控制字符串是否要求使用生物识别
     *
     * @param accessControl 访问控制配置字符串
     * @return 是否需要生物识别
     */
    public static boolean getUseBiometry(@AccessControl @Nullable final String accessControl) {
        // 检查是否属于需要生物识别的访问控制类型
        return AccessControl.BIOMETRY_ANY.equals(accessControl) ||
                AccessControl.BIOMETRY_CURRENT_SET.equals(accessControl) ||
                AccessControl.BIOMETRY_ANY_OR_DEVICE_PASSCODE.equals(accessControl) ||
                AccessControl.BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE.equals(accessControl);
    }


    /**
     * 将加密存储实例添加到映射表中
     */
    private void addCipherStorageToMap(@NonNull final CipherStorage cipherStorage) {
        cipherStorageMap.put(cipherStorage.getCipherStorageName(), cipherStorage);
    }


    /**
     * 从当前存储解密凭证数据，如果存储不匹配则执行迁移
     *
     * @param alias     条目别名
     * @param current   当前首选加密存储
     * @param resultSet 存储的加密数据
     * @param rules     安全规则（是否允许自动升级）
     * @return 解密结果
     */
    @NonNull
    private CipherStorage.DecryptionResult decryptCredentials(@NonNull final String alias,
                                                              @NonNull final CipherStorage current,
                                                              @NonNull final PrefsStorage.ResultSet resultSet,
                                                              @Rules @NonNull final String rules)
            throws CryptoFailedException, KeyStoreAccessException {
        final String storageName = resultSet.cipherStorageName;

        // 情况1：数据使用当前加密存储加密 - 直接解密
        if (storageName.equals(current.getCipherStorageName())) {
            return decryptToResult(alias, current, resultSet);
        }

        // 情况2：数据使用旧加密存储加密 - 需要迁移
        final CipherStorage oldStorage = getCipherStorageByName(storageName);
        if (null == oldStorage) {
            throw new KeyStoreAccessException("无法找到存储 '" + storageName + "' 或该加密方式不可用");
        }

        // 使用旧存储解密数据
        final DecryptionResult decryptionResult = decryptToResult(alias, oldStorage, resultSet);

        // 如果启用了自动升级规则，迁移到新存储
        if (Rules.AUTOMATIC_UPGRADE.equals(rules)) {
            try {
                migrateCipherStorage(alias, current, oldStorage, decryptionResult);
            } catch (CryptoFailedException e) {
                // 禁止降级到安全性更低的存储
                Log.w(KEYCHAIN_MODULE, "不允许迁移到安全性更低的存储，保留原存储");
            }
        }

        return decryptionResult;
    }

    /**
     * 使用指定存储解密数据
     */
    @NonNull
    private DecryptionResult decryptToResult(@NonNull final String alias,
                                             @NonNull final CipherStorage storage,
                                             @NonNull final PrefsStorage.ResultSet resultSet)
            throws CryptoFailedException {
        // 获取解密处理器（处理交互式解锁等场景）
        final CipherStorage.DecryptionResultHandler handler = getInteractiveHandler(storage);
        // 执行解密操作
        storage.decrypt(handler, alias, resultSet.username, resultSet.password, SecurityLevel.ANY);

        // 检查并处理解密错误
        CryptoFailedException.reThrowOnError(handler.getError());

        // 确保有解密结果
        if (null == handler.getResult()) {
            throw new CryptoFailedException("无解密结果且无错误信息！发生严重错误");
        }

        return handler.getResult();
    }

    /**
     * 获取交互式处理器（处理系统级解锁请求）
     */
    @NonNull
    protected CipherStorage.DecryptionResultHandler getInteractiveHandler(@NonNull final CipherStorage current) {
        // 默认实现使用非交互式处理器（适用于AES-CBC）
        return new CipherStorageKeystoreAesCbc.NonInteractiveHandler();
    }

    /**
     * 将密钥从旧存储迁移到新存储
     * 1. 使用新存储重新加密数据
     * 2. 更新存储记录
     * 3. 从旧存储删除密钥
     */
    void migrateCipherStorage(@NonNull final String service,
                              @NonNull final CipherStorage newCipherStorage,
                              @NonNull final CipherStorage oldCipherStorage,
                              @NonNull final DecryptionResult decryptionResult)
            throws KeyStoreAccessException, CryptoFailedException {
        // 使用新存储加密（保持相同安全级别）
        final CipherStorage.EncryptionResult encryptionResult = newCipherStorage.encrypt(
                service, decryptionResult.username, decryptionResult.password,
                decryptionResult.getSecurityLevel());

        // 更新存储记录
        prefsStorage.storeEncryptedEntry(service, encryptionResult);

        // 从旧存储中删除密钥
        oldCipherStorage.removeKey(service);
    }

    /**
     * 获取当前API级别的最佳加密存储
     * （选择能力级别最高且支持当前API的存储）
     */
    @NonNull
    CipherStorage getCipherStorageForCurrentAPILevel() throws CryptoFailedException {
        return getCipherStorageForCurrentAPILevel(true); // 默认允许使用生物识别
    }

    /**
     * 获取当前API级别的最佳加密存储（可控制是否使用生物识别）
     * 选择策略：
     * 1. 筛选支持当前API级别的存储
     * 2. 选择能力级别(capabilityLevel)最高的存储
     */
    @NonNull
    CipherStorage getCipherStorageForCurrentAPILevel(final boolean useBiometry)
            throws CryptoFailedException {
        final int currentApiLevel = Build.VERSION.SDK_INT;
        CipherStorage foundCipher = null;

        // 遍历所有可用存储
        for (CipherStorage variant : cipherStorageMap.values()) {
            // 检查是否支持当前API
            final int minApiLevel = variant.getMinSupportedApiLevel();
            final boolean isSupportedApi = (minApiLevel <= currentApiLevel);
            if (!isSupportedApi) continue;

            // 检查生物识别支持
            if (useBiometry && !variant.isBiometrySupported()) continue;

            // 选择能力级别最高的存储
            if (foundCipher == null || variant.getCapabilityLevel() > foundCipher.getCapabilityLevel()) {
                foundCipher = variant;
            }
        }

        // 无可用存储时抛出异常
        if (foundCipher == null) {
            throw new CryptoFailedException("不支持的Android SDK版本: " + Build.VERSION.SDK_INT);
        }

        return foundCipher;
    }

    /**
     * 检查用户名/密码是否为空
     */
    public static void throwIfEmptyLoginPassword(@Nullable final String username,
                                                 @Nullable final String password)
            throws EmptyParameterException {
        if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
            throw new EmptyParameterException("用户名或密码为空");
        }
    }

    /**
     * 检查存储是否满足要求的安全级别
     *
     * @throws CryptoFailedException 如果存储的安全级别不足
     */
    public static void throwIfInsufficientLevel(@NonNull final CipherStorage storage,
                                                @NonNull final SecurityLevel level)
            throws CryptoFailedException {
        if (!storage.securityLevel().satisfiesSafetyThreshold(level)) {
            throw new CryptoFailedException(
                    String.format("加密存储安全性不足。要求: %s, 实际: %s",
                            level.name(), storage.securityLevel().name()));
        }
    }

    /**
     * 根据名称获取加密存储实例
     */
    @Nullable
    CipherStorage getCipherStorageByName(@KnownCiphers @NonNull final String knownName) {
        return cipherStorageMap.get(knownName);
    }

    /**
     * 检查当前设备是否支持安全硬件
     */
    boolean isSecureHardwareAvailable() {
        try {
            return getCipherStorageForCurrentAPILevel().supportsSecureHardware();
        } catch (CryptoFailedException e) {
            return false; // 发生异常时默认不支持
        }
    }

    /**
     * 获取当前存储的安全级别
     */
    @NonNull
    private SecurityLevel getSecurityLevel(final boolean useBiometry) {
        try {
            final CipherStorage storage = getCipherStorageForCurrentAPILevel(useBiometry);

            // 安全级别评估
            if (storage.supportsSecureHardware()) {
                return SecurityLevel.SECURE_HARDWARE;
            } else if (storage.securityLevel().satisfiesSafetyThreshold(SecurityLevel.SECURE_SOFTWARE)) {
                return SecurityLevel.SECURE_SOFTWARE;
            } else {
                return SecurityLevel.ANY;
            }
        } catch (CryptoFailedException e) {
            return SecurityLevel.ANY; // 异常时返回最低安全级别
        }
    }

    /**
     * 处理服务别名为空的情况
     */
    @NonNull
    private static String getAliasOrDefault(@Nullable final String service) {
        return service == null ? EMPTY_STRING : service;
    }
}
